package com.hy.proxy.core.util;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.hy.proxy.common.CipherUtils;
import com.hy.proxy.common.JsonUtils;
import com.hy.proxy.common.ThreadPoolUtils;
import com.hy.proxy.core.entity.Proxy;
import com.hy.proxy.core.entity.SS;
import com.hy.proxy.core.entity.SSR;
import com.hy.proxy.core.entity.V2ray;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.chrome.ChromeDriver;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: hy
 * @Date: Created in 2019/9/25/23:58
 * @Description: 核心类，负责发送和保存所有网站、链接信息
 * @Version: 1.0
 */
@Slf4j
public class Site {

    //TODO 可使用枚举类型成员设置方法 优化
    public static final String FREE_V2RAY = "https://view.freev2ray.org/";
    public static final String FREE_V2RAY_QUERY_V2 = "span#ip,span#port,span#uuid,#intro > div > div > header > p:nth-child(6),button.copybtn";

    public static final String FREE_SS = "https://free-ss.site:443";
    public static final String FREE_SS_DATA = "https://free-ss.site/data.php:443";

    public static final String SSRTOOL = "https://tays.ml/tool/";
    public static final String[] SSRTOOL_PAGE = {"https://tays.ml/tool/api/free_ssr?page=1&limit=90", "https://tays.ml/tool/api/free_ssr?page=2&limit=90"};

    //    ----------------------------- 这三个List的作用就是保持数据的准确性
    /**
     * 保存所有V2网站的V2ray链接
     */
    public volatile static List<Proxy> v2List = new ArrayList<>();
    /**
     * 保存所有ss网站的ss链接
     */
    public volatile static List<Proxy> ssList = new ArrayList<>();
    /**
     * 保存所有ssr网站的ssr链接
     */
    public volatile static List<Proxy> ssrList = new ArrayList<>();

    //TODO 计划未来使用此方法替代 getProxyList 实现通过方法名称自动归类类型。
//    private static String getData(Proxy.ProxyType make) {
//
//        Map<Proxy.ProxyType, List<Method>> s = new Hashtable<>();
//
//        System.out.println(Site.class.getMethods()[0].getName());
//
//
//        return "";
//    }

    /**
     * 通用发送请求获取返回Proxy列表的方法
     *
     * @param type 要获取的Proxy类型
     * @return
     */
    public static List<Proxy> getProxyList(Proxy.ProxyType type) {

        if (type == Proxy.ProxyType.SSR) {

            ssrList.clear();

            sendRequestBySsrTool();

            return ssrList;

        } else if (type == Proxy.ProxyType.V2RAY) {

            v2List.clear();

            ThreadPoolUtils.Instance.executor(Site::sendRequestByFreeV2ray);

            sendRequestByFreeSs(type);

            return v2List;

        } else {

            ssList.clear();

            sendRequestByFreeSs(type);

            return ssList;
        }
    }

    /**
     * 向freeV2ray发送请求并解析源码，获取Proxy对象
     */
    public static List<Proxy> sendRequestByFreeV2ray() {

        try {

            Document document = Jsoup.connect(FREE_V2RAY).timeout(500000).get();
            Elements es = document.select(FREE_V2RAY_QUERY_V2);
            //截取加密部分
            String base64Str = es.get(4).attr("data-clipboard-text").substring("vmess://".length());
            //解密
            String v2Json = CipherUtils.BASE64.decoder(base64Str);
            //解析json构建一个V2对象
            V2ray v2ray = JsonUtils.getGson().fromJson(v2Json, new TypeToken<V2ray>() {
            }.getType());
            v2List.add(v2ray);

        } catch (IOException e) {
            log.error(e.getMessage());
        }

        return v2List;
    }

    /**
     * 向Free-ss发送请求并解析数据
     *
     * @return 代理对象列表
     */
    public static List<Proxy> sendRequestByFreeSs(Proxy.ProxyType type) {

        ChromeDriver wd = SeleniumUtils.getChromeDriver();

        //传入请求,获取源码
        String sourceCode = null;

        sourceCode = SeleniumUtils.getPageSource(wd, FREE_SS);

        wd.close();

        if (type == Proxy.ProxyType.SS) {
            return Site.parseSourceByFreeSs(sourceCode, Proxy.ProxyType.SS);
        } else {
            return Site.parseSourceByFreeSs(sourceCode, Proxy.ProxyType.V2RAY);
        }

    }


    /**
     * 向SsrTool发送请求并解析数据
     * 此网站特性：
     * 1.请求中的limit参数不能大于90,如果大于90将返回错误json
     * 2.浏览器必须先访问站点等数据加载完毕以后才能调用接口获取JSON，暂不知是什么原因。
     * 原因就是每次发送请求时，后台都会判断 __cfduid 和 JSESSIONID 这个两个Cookie如果和页面生成的不一致则返回空。
     * 注意：该方法为暂时为替代方法，如果Jsoup和HttpClient无法解决，则使用此方法。
     *
     * @return
     */
    public static List<Proxy> sendRequestBySsrTool() {

        ChromeDriver wd = SeleniumUtils.getChromeDriver();

        try {

            //因网站特性原因，需要请求三次。
            //第一次让页面加载数据
            SeleniumUtils.getPageSource(wd, SSRTOOL);

            //等5秒让ajax数据加载完毕
            Thread.sleep(3000);

            //第二三次请求接口获取json
            for (int i = 0; i < 2; i++) {

                wd.get(SSRTOOL_PAGE[i]);

                //获取请求结果
                String responsePage = wd.findElementByTagName("body").getText();

                parseJsonBySsrTool(responsePage);

            }

        } catch (InterruptedException e) {
            log.error(e.getMessage());
        } finally {
            wd.close();
        }

        return ssrList;
    }

    /**
     * 解析json数据并封装至SSR对象
     *
     * @param json
     * @return
     */
    private static List<Proxy> parseJsonBySsrTool(String json) {

        JSONObject jsonObject = JSONObject.fromObject(json);

        String data = jsonObject.get("data").toString();

        Gson gson = JsonUtils.getGson();

        List<SSR> ssrTempList = gson.fromJson(data, new TypeToken<List<SSR>>() {
        }.getType());

        if (ssrTempList != null && ssrTempList.size() != 0) {
            ssrList.addAll(ssrTempList);
        }

        return ssrList;
    }

    /**
     * 解析Free-ss的源码，获取Proxy对象
     *
     * @param sourceCode 网页源码
     * @param make       将返回不同的数据
     * @return
     */
    private static List<Proxy> parseSourceByFreeSs(String sourceCode, Proxy.ProxyType make) {

        Map<Proxy.ProxyType, String[]> cssQueryMap = new HashMap<>(2);
        cssQueryMap.put(Proxy.ProxyType.V2RAY, new String[]{"#tbv2 > tbody > tr", "td:nth-child(n+2):not(:nth-last-child(2))"});
        cssQueryMap.put(Proxy.ProxyType.SS, new String[]{"#tbss > tbody > tr", "td:nth-child(n+2):not(:nth-last-child(1))"});

        //解析源码并获取body体内容
        Element body = Jsoup.parse(sourceCode).body();

        //获取ss列表 tr
        Elements proxys = body.select(cssQueryMap.get(make)[0]);

        //循环获取一个 tr
        for (Element proxyAttribute : proxys) {

            //获取一个属性列表 td    (不选第一个和最后一个)
            Elements proxyAttributeList = proxyAttribute.select(cssQueryMap.get(make)[1]);

            Proxy proxy = null;

            //组装对象
            if (make == Proxy.ProxyType.V2RAY) {
                proxy = new V2ray();
                ((V2ray) proxy).setAdd(proxyAttributeList.get(0).text());
                ((V2ray) proxy).setPort(proxyAttributeList.get(1).text());
                ((V2ray) proxy).setId(proxyAttributeList.get(2).text());
                ((V2ray) proxy).setNet(proxyAttributeList.get(3).text());
                ((V2ray) proxy).setHost(proxyAttributeList.get(4).text());
                ((V2ray) proxy).setTls(proxyAttributeList.get(5).text());
                v2List.add(proxy);
            } else {
                proxy = new SS();
                ((SS) proxy).setCountry(proxyAttributeList.get(5).text());
                ((SS) proxy).setData(proxyAttributeList.get(4).text());
                ((SS) proxy).setMethod(proxyAttributeList.get(2).text());
                ((SS) proxy).setPassword(proxyAttributeList.get(3).text());
                ((SS) proxy).setServer(proxyAttributeList.get(0).text());
                ((SS) proxy).setServerPort(Integer.parseInt(proxyAttributeList.get(1).text()));
                ssList.add(proxy);
            }
        }

        return make == Proxy.ProxyType.V2RAY ? v2List : ssList;
    }

}
